﻿using Castle.DynamicProxy;
using Newtonsoft.Json;
using Syspetro.Core.Extensions;
using Syspetro.Core.SysInterface;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace Syspetro.Core.AopService
{
    public abstract class BaseInterceptorAttribute : Attribute
    {
        public abstract Action DoIn(IInvocation invocation, Action action);
        public abstract Action Do(IInvocation invocation, Action action);
        public abstract Action DoOut(IInvocation invocation, Action action);
    }
    /// <summary>
    /// 计时器
    /// </summary>
    public class MonitorAttribute : BaseInterceptorAttribute
    {
        public override Action Do(IInvocation invocation, Action action)
        {
            return () =>
            {
                Stopwatch stopwatch = new();
                Console.WriteLine("This is MonitorAttribute in");
                stopwatch.Start();
                action.Invoke();
                stopwatch.Stop();
                Console.WriteLine($"本次方法花费时间{stopwatch.ElapsedMilliseconds}ms");
                Console.WriteLine("This is MonitorAttribute out");
            };
        }

        public override Action DoIn(IInvocation invocation, Action action)
        {
            return action;
        }

        public override Action DoOut(IInvocation invocation, Action action)
        {
            return action;
        }
    }
    /// <summary>
    /// 加锁
    /// </summary>
    public class LockerAttribute : BaseInterceptorAttribute
    {
        /// <summary>
        /// 获取锁等待时间,单位秒
        /// </summary>
        public int Expiry { get; set; } = 5;
        /// <summary>
        /// 加锁时长,单位秒
        /// </summary>
        public int LockTime { get; set; } = 1;
        public string Key { get; set; }
        private ILock locker;

        private TimeSpan expiry = default;
        private TimeSpan ExpirySpan
        {
            get
            {
                if (expiry == default)
                    expiry = new TimeSpan(Expiry / 3600, Expiry / 60, Expiry % 60);
                return expiry;
            }
        }
        private TimeSpan lockTime = default;
        private TimeSpan LockTimeSpan
        {
            get
            {
                if (lockTime == default)
                    lockTime = new TimeSpan(LockTime / 3600, LockTime / 60, LockTime % 60);
                return lockTime;
            }
        }

        public override Action Do(IInvocation invocation, Action action)
        {
            if (Expiry != default)//获取锁
            {
                if (Task.Run(() => { return locker.GetAwait(Key, ExpirySpan); }).Result)
                {
                    return LockDoWork(Key, LockTimeSpan, action, locker);
                }
            }
            else
            {
                if (locker.Get(Key))
                {
                    return LockDoWork(Key, LockTimeSpan, action, locker);
                }
            }
            return () =>
            {
                throw new("获取锁失败");
            };
        }
        private static Action LockDoWork(string Key, TimeSpan LockTime, Action action, ILock locker)
        {
            if (!locker.Set(Key, LockTime))
            {
                Console.WriteLine($"加{Key}锁失败并继续执行");
            }
            return () =>
            {
                action.Invoke();
            };
        }

        public override Action DoIn(IInvocation invocation, Action action)
        {
            locker = InternalApp.ServicesProvider.GetService(typeof(ILock)) as ILock;
            return action;
        }

        public override Action DoOut(IInvocation invocation, Action action)
        {
            locker.Remove(Key);
            return action;
        }
    }
    /// <summary>
    /// 缓存
    /// </summary>
    public class CacherAttribute : BaseInterceptorAttribute
    {
        /// <summary>
        /// 缓存过期时间,单位秒
        /// </summary>
        public int Expiry { get; set; }
        public string Key { get; set; }
        private ICache Cache;
        private string jsondate;
        private string _key;
        public override Action Do(IInvocation invocation, Action action)
        {
            if (string.IsNullOrEmpty(jsondate))
            {
                return () =>
                {
                    action.Invoke();
                };
            }
            else
            {
                return () =>
                {
                    var type = invocation.Method.ReturnType;
                    var retype = type.GenericTypeArguments.First();
                    if (type.BaseType == typeof(Task))
                    {
                        throw new Exception("不支持异步");
                    }
                    else
                    {
                        invocation.ReturnValue = JsonConvert.DeserializeObject(jsondate, type);
                    }
                };
            }
        }

        public override Action DoIn(IInvocation invocation, Action action)
        {
            Cache = InternalApp.ServicesProvider.GetService(typeof(ICache)) as ICache;

            if (invocation.Method.ReturnType.BaseType == typeof(Task))
                this._key = Key + invocation.Method.ReturnType.ToString().Replace("System.Threading.Tasks.Task", "");
            else
                this._key = Key + invocation.Method.ReturnType.Name;

            jsondate = Cache.Get(_key);
            return action;
        }

        public override Action DoOut(IInvocation invocation, Action action)
        {
            var timespan = new TimeSpan(Expiry / 3600, Expiry / 60, Expiry % 60);
            if (string.IsNullOrEmpty(jsondate))
            {
                if (invocation.Method.ReturnType.BaseType == typeof(Task))
                {
                    invocation.Proceed();
                    Cache.Set(_key, JsonConvert.SerializeObject(((dynamic)invocation.ReturnValue).Result), timespan);
                }
                else
                    Cache.Set(_key, invocation.ReturnValue.ToJson(), timespan);

            }
            return action;
        }
    }
}
